package visitor;

import syntaxtree.*;

public class TypeDepthFirstVisitor implements TypeVisitor {


  // MainClass m;
  // ClassDeclList cl;
  public Type visit(Program n) {
    n.m.accept(this);
    for ( int i = 0; i < n.cl.size(); i++ ) {
        n.cl.elementAt(i).accept(this);
    }
    return new BooleanType();
  }

  // Identifier i1,i2;
  // Statement s;
  public Type visit(MainClass n) {
    n.i1.accept(this);
    n.i2.accept(this);
    n.s.accept(this);
    return new BooleanType();
  }
  
  // Identifier i;
  // VarDeclList vl;
  // MethodDeclList ml;
  // class id  { v1, m1}
  public Type visit(ClassDeclSimple n) {
    n.i.accept(this);
    for ( int i = 0; i < n.vl.size(); i++ ) {
        n.vl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.ml.size(); i++ ) {
        n.ml.elementAt(i).accept(this);
    }
    return new BooleanType();
  }



  // Identifier i;
  // Identifier j;
  // VarDeclList vl;
  // MethodDeclList ml;
  // class id extends id { varDecl* MethodDecl*}
  public Type visit(ClassDeclExtends n) {
    n.i.accept(this);
    n.j.accept(this);
    for ( int i = 0; i < n.vl.size(); i++ ) {
        n.vl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.ml.size(); i++ ) {
        n.ml.elementAt(i).accept(this);
    }
    return new BooleanType();
  }

  // Type t;
  // Identifier i;
  // int x; 
  public Type visit(VarDecl n) {
    n.t.accept(this);
    n.i.accept(this);
    return new BooleanType();
  }

  // Type t;
  // Identifier i;
  // FormalList fl;
  // VarDeclList vl;
  // StatementList sl;
  // Exp e;
  // public Type id (FormalList) 
  //    {VarDecl* Statement* return Exp ;}
  public Type visit(MethodDecl n) {
    Type t1 = n.t.accept(this);
    n.i.accept(this);
    for ( int i = 0; i < n.fl.size(); i++ ) {
        n.fl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.vl.size(); i++ ) {
        n.vl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.sl.size(); i++ ) {
        n.sl.elementAt(i).accept(this);
    }
    Type t2 = n.e.accept(this);
    if(t1 != t2)
      error.complain("tipo de retorno nao bate");
    return new BooleanType();
  }

  // Type t;
  // Identifier i;
  // precisa da tabela de simbolos
  public Type visit(Formal n) {
    Type t1 = n.t.accept(this);
    Type t2 = n.i.accept(this);
    if(t1 != t2) 
      error.complain("tipos de variavel e identificado nao batem")
    return new BooleanType();
  }

  
  // onde isso eh usado?
  public Type visit(IntArrayType n) {
    return new IntArrayType();
  }

  public Type visit(BooleanType n) {
    return new BooleanType();
  }

  public Type visit(IntegerType n) {
    return new ();
  }

  // String s;
  // nome de classe!
  public Type visit(IdentifierType n) {
    return new IdentifierType();
  }

  // StatementList sl;
  public Type visit(Block n) {
    for ( int i = 0; i < n.sl.size(); i++ ) {
        n.sl.elementAt(i).accept(this);
    }
    return new BooleanType();
  }

  // Exp e;
  // Statement s1,s2;
  // if(e) s1, else s2;
  public Type visit(If n) {
    n.e.accept(this);
    n.s1.accept(this);
    n.s2.accept(this);
    return new BooleanType());
  }

  // Exp e;
  // Statement s;
  // while(e) {s};
  public Type visit(While n) {
    n.e.accept(this);
    n.s.accept(this);
    return new BooleanType();
  }

  
  // Exp e;
  public Type visit(Print n) {
    n.e.accept(this);
    return new BooleanType();
  }
  

  // Identifier i;
  // Exp e;
  // i = e
  public Type visit(Assign n) {
    n.i.accept(this);
    n.e.accept(this);
    return new BooleanType();
  }

  // Identifier i;
  // Exp e1,e2;
  // i[e1] = e2;
  public Type visit(ArrayAssign n) {
    Type t1 = n.i.accept(this);
    n.e1.accept(this);
    Type t3 = n.e2.accept(this);
    if(!(t1 instanceof IntArrayType))
      error.complain("a atribuicao nao e sob um vetor de inteiros");
    if(!(t3 instanceof IntegerType))
      error.complain("elemento a adicionar ao vetor nao eh inteiro");
    return new BooleanType();
  }


  // Exp e1,e2;
  public Type visit(And n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof BooleanType)) 
      error.complain("operando a esquerda de && nao eh booleano");
    if(!(t2 instanceof BooleanType))
      error.complain("operando a direita de && nao eh booleano");
    return new BooleanType();
  }
  // Exp e1,e2;
  public Type visit(LessThan n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof IntegerType)) 
      error.complain("operando a esquerda de < nao eh int");
    if(!(t2 instanceof IntegerType))
      error.complain("operando a direita de < nao eh int");
    return new BooleanType();
  }

  // Exp e1,e2;
  public Type visit(Plus n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof IntegerType)) 
      error.complain("operando a esquerda de + nao eh int");
    if(!(t2 instanceof IntegerType))
      error.complain("operando a direita de + nao eh int");
    return new IntegerType();
   }

  // Exp e1,e2;
  public Type visit(Minus n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof IntegerType)) 
      error.complain("operando a esquerda de - nao eh int");
    if(!(t2 instanceof IntegerType))
      error.complain("operando a direita de - nao eh int");
    return new IntegerType();
  }

  // Exp e1,e2;
  public Type visit(Times n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof IntegerType)) 
      error.complain("operando a esquerda de * nao eh int");
    if(!(t2 instanceof IntegerType))
      error.complain("operando a direita de * nao eh int");
    return new IntegerType();
  }
 // Exp e1,e2;
  // e1[e2]
  public Type visit(ArrayLookup n) {
    Type t1 = n.e1.accept(this);
    Type t2 = n.e2.accept(this);
    if(!(t1 instanceof IntArrayType))
      error.complain("tentando acesso a vetor que nao eh vetor");
    if(!(t2 instanceof IntegerType))
      error.complain("indice de vetor deve ser inteiro");
    return null;
  }


  // Exp e;
  // e.length
  public Type visit(ArrayLength n) {
    Type t = n.e.accept(this);
    if(!(t instanceof IntArrayType))
      error.complain("nao ha length para nao-vetor");
    return new IntegerType();
  }

  // Exp e;
  // Identifier i;
  // ExpList el;
  // precisa da tabela de metodos
  // e.i(el)
  public Type visit(Call n) {
    Type t1 = n.e.accept(this);

    n.i.accept(this);
    for ( int i = 0; i < n.el.size(); i++ ) {
        n.el.elementAt(i).accept(this);
    }
    return null;
  }
 

  // int i;
  public Type visit(IntegerLiteral n) {
    return new IntegerType();
  }

  public Type visit(True n) {
    return new BooleanType();
  }

  public Type visit(False n) {
    return new BooleanType();
  }


  // Exp e;
  // new int[]
  public Type visit(NewArray n) {
    Type t = n.e.accept(this);
    if(!(t instanceof IntegerType))
      error.complain("tamanho de vetor deve ser inteiro")
  }

  // Exp e;
  public Type visit(Not n) {
    Type t = n.e.accept(this);
    if(!(t instanceof BooleanType))
      error.complain("operando da negacao deve ser booleano");
    return new BooleanType();
  }
  

  // String s;
  // precisa da tabela
  public Type visit(IdentifierExp n) {
    return null;
  }

  // Identifier i;
  public Type visit(NewObject n) {
    //precisa da tabela de simbolos
    return n.i.accept(this);
  }

  //como pega o tipo do this??
  public Type visit(This n) {
    return null;
  }

  // String s;
  public Type visit(Identifier n) {
    //precisa da tabela de simbolos
    return null;
  }
